﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Expand;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Reflection;
using System.Drawing;
using System.Windows.Forms;
using Ultima;
using MouseKeyboardLibrary;

namespace SpellCaster3.Core
{
    [Serializable]
    public class ButtonMacro : IExecutable, IXmlSerializable, ISerializable, INotifyPropertyChanged, INotifyPropertyChanging
    {
        public static readonly SortedSet<Keys> AllowedKeys = new SortedSet<Keys>();

        static ButtonMacro()
        {
            for (Keys i = Keys.D0; i <= Keys.D9; ++i)
                AllowedKeys.Add(i);
            for (Keys i = Keys.F1; i <= Keys.F12; ++i)
                AllowedKeys.Add(i);
            for (Keys i = Keys.A; i <= Keys.Z; ++i)
                AllowedKeys.Add(i);
            AllowedKeys.Add(Keys.Up);
            AllowedKeys.Add(Keys.Left);
            AllowedKeys.Add(Keys.Right);
            AllowedKeys.Add(Keys.Down);

            AllowedKeys.Add(Keys.Home);
            AllowedKeys.Add(Keys.End);
            AllowedKeys.Add(Keys.PageUp);
            AllowedKeys.Add(Keys.PageDown);

            AllowedKeys.Add(Keys.Enter);
            AllowedKeys.Add(Keys.Tab);

            AllowedKeys.Add(Keys.PrintScreen);
            AllowedKeys.Add(Keys.Escape);
            AllowedKeys.Add(Keys.Back);

            AllowedKeys.Add(Keys.Delete);
            AllowedKeys.Add(Keys.Insert);

            // Modifiers
            AllowedKeys.Add(Keys.Alt);
            AllowedKeys.Add(Keys.Control);
            AllowedKeys.Add(Keys.Shift);
        }

        #region Private fields
        private int _GumpIndex;
        private int _HueIndex;
        private string _Name;
        private string _Alias;
        private string _KeyName;
        private string _Command1;
        private string _Command2;
        private string _Command3;
        private Point _Position;
        private Keys _KeysCombo;
        private string _Description;
        #endregion

        #region Properties
        public int GumpIndex
        {
            get { return _GumpIndex; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _GumpIndex = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public int HueIndex
        {
            get { return _HueIndex; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _HueIndex = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Name
        {
            get { return _Name; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Name = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Alias
        {
            get { return _Alias; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Alias = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string KeyName
        {
            get { return _KeyName; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _KeyName = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Command1
        {
            get { return _Command1; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Command1 = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Command2
        {
            get { return _Command2; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Command2 = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Command3
        {
            get { return _Command3; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Command3 = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public Point Position
        {
            get { return _Position; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Position = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public Keys KeysCombo
        {
            get { return _KeysCombo; }
            set
            {
                if (!ValidKeysCombo(value))
                    throw new ArgumentException("value contains invalid keys");

                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _KeysCombo = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public string Description
        {
            get { return _Description; }
            set
            {
                OnPropertyChanging(MethodBase.GetCurrentMethod());
                _Description = value;
                OnPropertyChanged(MethodBase.GetCurrentMethod());
            }
        }

        public static bool ValidKeysCombo(Keys keys)
        {
            Keys tmp = keys & ~(Keys.Shift | Keys.Control | Keys.Alt);

            if (tmp == Keys.None) return true;
            return AllowedKeys.Contains(tmp);
        }

        public string KeysComboReadableString
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                if (KeysCombo == Keys.None) return string.Empty;
                if (KeysCombo.HasFlag(Keys.Shift)) sb.Append("Shift+");
                if (KeysCombo.HasFlag(Keys.Control)) sb.Append("Ctrl+");
                if (KeysCombo.HasFlag(Keys.Alt)) sb.Append("Alt+");
                Keys tmp = KeysCombo & ~(Keys.Shift | Keys.Control | Keys.Alt);
                if (tmp == Keys.None) return sb.ToString();
                if (!AllowedKeys.Contains(tmp)) return string.Empty;
                sb.Append(Enum.GetName(tmp.GetType(), tmp));
                return sb.ToString();
            }
        }

        public List<Keys> KeysComboModifiers
        {
            get
            {
                List<Keys> modifiers = new List<Keys>(3);
                if (KeysCombo.HasFlag(Keys.Shift)) modifiers.Add(Keys.Shift);
                if (KeysCombo.HasFlag(Keys.Control)) modifiers.Add(Keys.Control);
                if (KeysCombo.HasFlag(Keys.Alt)) modifiers.Add(Keys.Alt);
                return modifiers;
            }
        }

        public Keys KeysComboKey
        {
            get
            {
                Keys tmp = KeysCombo & ~(Keys.Shift | Keys.Control | Keys.Alt);
                if (tmp == Keys.None) return Keys.None;
                if (!AllowedKeys.Contains(tmp)) return Keys.None;
                return tmp;
            }
        }

        public bool KeysComboHasKey { get { return KeysComboKey != Keys.None; } }
        #endregion

        public void Execute()
        {
            bool command1Done, command2Done, command3Done, keysComboHasKey;
            command1Done = !string.IsNullOrWhiteSpace(Command1);
            command2Done = !string.IsNullOrWhiteSpace(Command2);
            command3Done = !string.IsNullOrWhiteSpace(Command3);
            keysComboHasKey = KeysComboHasKey;

            if (Client.Running)
            {
                bool bringToTopResult = false;
                if (command1Done) Client.SendText(Command1);
                if (command2Done) Client.SendText(Command2);
                if (command3Done) Client.SendText(Command3);
                if (command1Done || command2Done || command3Done || keysComboHasKey) bringToTopResult = Client.BringToTop();

                if (keysComboHasKey && bringToTopResult)
                {
                    foreach (Keys key in KeysComboModifiers)
                        KeyboardSimulator.KeyDown(key);

                    KeyboardSimulator.KeyPress(KeysComboKey);

                    foreach (Keys key in KeysComboModifiers)
                        KeyboardSimulator.KeyUp(key);
                }
                else if (keysComboHasKey && !bringToTopResult)
                    ErrorForm.Show("Impossibile dare il focus alla finestra di Ultima Online");
                
                if (!(command1Done || command2Done || command3Done || keysComboHasKey))
                    MessageBox.Show("Nessun comando da eseguire", "Informazione", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                string keyscomboreadablestring = KeysComboReadableString;
                MessageBox.Show(string.Format("Client non in esecuzione, comandi che dovrebbero essere eseguiti: {0}{1}{2}{3}{4}{5}\n{6}", command1Done ? "\n1)" : "", command1Done ? Command1 : "", command2Done ? "\n2)" : "", command2Done ? Command2 : "", command3Done ? "\n3)" : "", command3Done ? Command3 : "", string.IsNullOrWhiteSpace(keyscomboreadablestring) ? "" : keyscomboreadablestring), "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }


        public void ReadXml(XmlReader reader)
        {
            GumpIndex = reader.ReadElementContentAsInt();
            HueIndex = reader.ReadElementContentAsInt();

            Name = reader.ReadElementContentAsString();
            Alias = reader.ReadElementContentAsString();
            KeyName = reader.ReadElementContentAsString();
            Command1 = reader.ReadElementContentAsString();
            Command2 = reader.ReadElementContentAsString();
            Command3 = reader.ReadElementContentAsString();
            int x, y;
            x = reader.ReadElementContentAsInt();
            y = reader.ReadElementContentAsInt();
            Position = new Point(x, y);
            if (Profile.FoundVersion == Version.Parse("1.0.2.2"))
                KeysCombo = (Keys)Enum.ToObject(typeof(Keys), reader.ReadElementContentAsInt());
            if (Profile.FoundVersion >= Version.Parse("1.0.2.3"))
                KeysCombo = (Keys)Enum.Parse(typeof(Keys), reader.ReadElementContentAsString());
            if (Profile.FoundVersion >= Version.Parse("1.0.2.1"))
                Description = reader.ReadElementContentAsString();
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteElementString("GumpIndex", GumpIndex.ToString());
            writer.WriteElementString("HueIndex", HueIndex.ToString());

            writer.WriteStartElement("Name");
            writer.WriteCData(Name);
            writer.WriteEndElement();

            writer.WriteStartElement("Alias");
            writer.WriteCData(Alias);
            writer.WriteEndElement();

            writer.WriteStartElement("KeyName");
            writer.WriteCData(KeyName);
            writer.WriteEndElement();

            writer.WriteStartElement("Command1");
            writer.WriteCData(Command1);
            writer.WriteEndElement();

            writer.WriteStartElement("Command2");
            writer.WriteCData(Command2);
            writer.WriteEndElement();

            writer.WriteStartElement("Command3");
            writer.WriteCData(Command3);
            writer.WriteEndElement();

            writer.WriteElementString("X", Position.X.ToString());

            writer.WriteElementString("Y", Position.Y.ToString());

            writer.WriteStartElement("KeysCombo");
            writer.WriteCData(KeysCombo.ToString());
            writer.WriteEndElement();

            writer.WriteStartElement("Description");
            writer.WriteCData(Description);
            writer.WriteEndElement();
        }

        public override string ToString()
        {
            return (string.IsNullOrWhiteSpace(Name) ? GetType().Name : Name);
        }

        //Serialize
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("GumpIndex", GumpIndex);
            info.AddValue("HueIndex", HueIndex);
            info.AddValue("Name", Name);
            info.AddValue("Alias", Alias);
            info.AddValue("KeyName", KeyName);
            info.AddValue("Command1", Command1);
            info.AddValue("Command2", Command2);
            info.AddValue("Command3", Command3);
            info.AddValue("X", Position.X);
            info.AddValue("Y", Position.Y);
            info.AddValue("KeysCombo", KeysCombo.ToString());
            info.AddValue("Description", Description);
        }

        public ButtonMacro()
        {
            HueIndex    = 1;
            GumpIndex   = SpellCaster3.Forms.GumpPicker.BaseRange.Minimum;
            Name        = string.Empty;
            Alias       = string.Empty;
            KeyName     = string.Empty;
            Command1    = string.Empty;
            Command2    = string.Empty;
            Command3    = string.Empty;
            Position    = new Point(0, 0);
            KeysCombo   = Keys.None;
            Description = string.Empty;
        }

        // Deserialize
        public ButtonMacro(SerializationInfo info, StreamingContext context)
            : this()
        {
            GumpIndex = info.GetInt32("GumpIndex");
            HueIndex = info.GetInt32("HueIndex");
            Name = info.GetString("Name");
            Alias = info.GetString("Alias");
            KeyName = info.GetString("KeyName");
            Command1 = info.GetString("Command1");
            Command2 = info.GetString("Command2");
            Command3 = info.GetString("Command3");
            int x, y;
            x = info.GetInt32("X");
            y = info.GetInt32("Y");
            Position = new Point(x, y);
            if (Profile.FoundVersion == Version.Parse("1.0.2.2"))
                KeysCombo = (Keys)Enum.ToObject(typeof(Keys), info.GetInt32("KeysCombo"));
            if (Profile.FoundVersion >= Version.Parse("1.0.2.3"))
                KeysCombo = (Keys)Enum.Parse(typeof(Keys), info.GetString("KeysCombo"));
            if (Profile.FoundVersion >= Version.Parse("1.0.2.1"))
                Description = info.GetString("Description");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(sender, e);
        }

        private void OnPropertyChanged(MethodBase property)
        {
            OnPropertyChanged(this, new PropertyChangedEventArgs(property.Name.Remove(0,4)));
        }

        public event PropertyChangingEventHandler PropertyChanging;

        protected void OnPropertyChanging(object sender, PropertyChangingEventArgs e)
        {
            if (PropertyChanging != null) PropertyChanging(sender, e);
        }

        private void OnPropertyChanging(MethodBase property)
        {
            OnPropertyChanging(this, new PropertyChangingEventArgs(property.Name.Remove(0, 4)));
        }
    }
}
